<?php
if(isset($GLOBALS['context']))	require_lib('openapi/OpenAPIRequest',false);
else	require_once dirname(__FILE__).'/../OpenAPIRequest.class.php';

/* other taobao url
define('TAIBAO_API_AUTHCODEHOST','http://open.taobao.com/isv/authorize.php?appkey=');
define('TAIBAO_API_SESSIONHOST','http://container.api.taobao.com/container?authcode=');
define('TAIBAO_API_SESSIONHOST_SANDBOX','http://container.api.tbsandbox.com/container?authcode=');
*/

/** 
 * 传送阿里接口[简称alibaba]API参数到网店,得到相应返回值。
 * @author alex  
 * @version v1.0  2012-3-8
 */
class AliRequest extends OpenAPIRequest {
	/**
	 *  API的网站地址
	 */	
	const HOST_URL='http://gw.open.china.alibaba.com/openapi/';
	
	/**
	 * @var array $app_token 应用app访问taobao的令牌，参数包括：<br/>
	 * string app_key : 必须，  API应用的标志符.<br/>
	 * string app_secret :必须， 用户登录appSecret，用于用户鉴权，将相关参数通过hmac加密为一个sign值<br/>
	 * string app_session   :可选，会话id，需要会话的接口必须提供 
	 */
	public $app_token=array();
	
	/**
	 * @var array $app_setting 应用app访问通用参数设置，一般不改变默认值，包括：<br/>
	 * string format：响应格式。xml,json，默认是 'json'。<br/>
	 */
	public $app_setting=array('_aop_defaultNamespace'=>'cn.alibaba.open', 
	                          '_aop_defaultVersion'=>'1', 
	                          '_aop_responseEncoding'=>'utf-8',
						      '_aop_requestFormat'=>'param2',
						      'format'=>'json'
					         );
	 
	private function get_uri_path($name,$appKey){
		return "{$this->app_setting['_aop_requestFormat']}/{$this->app_setting['_aop_defaultVersion']}/".
				"{$this->app_setting['_aop_defaultNamespace']}/{$name}/{$appKey}" ;
	}
	
    function __construct(){
    	parent::__construct();
    	$time_zone=@date_default_timezone_get();
    	if(! $time_zone || $time_zone=='UTC')
    	    date_default_timezone_set('Asia/Shanghai');
    }

	/**
	 * 得到taobo API的返回值，JSON,XML字符串。
	 * @param string $cmdid 要调用的api接口的cmdid字符串，如user.getUserInfo，如果为空，将返回false.
	 * @param array $params 要调用的api接口的参数数组，一般通用的参数不要传，如charset，format等。
	 * @return string|bool 调用的api接口得到的JSON,XML字符串。
	 */
	function &get_resp($cmdid,array $params){
		if(! $cmdid) return false; 
		if(! $this->app_token['app_key'] || ! $this->app_token['app_secret']) 
		   return false;
		
		$uri_path=$this->get_uri_path($cmdid,$this->app_token['app_key']);
		
		//组装param array.
		
		if(isset($this->app_token['app_session']))
		    $params['sessionId']=$this->app_token['app_session'];
		if($this->app_setting['format']!='json')
		    $params['_aop_responseFormat']=$this->app_setting['format'];	
		    	
		//$paramArr['_aop_timestamp']=round( microtime(true)*1000 );
		$params['_aop_signature']=$this->_createSign($this->app_token['app_secret'],$params,$uri_path);
		$url= self::HOST_URL . $uri_path; 
		$result=& $this->send($url,$params);
		return $result;
	}
	
	/**
	 * 得到 API的返回值，JSON,XML生成的数组。
	 * @param string $cmdid 要调用的api接口的cmdid字符串，如果为空，将返回false.
	 * @param array $paramArr 要调用的api接口的参数数组，一般通用的参数不要传，如charset，format等。
	 * @param string $resp_handle 用于额外处理get_resp函数返回字符串的函数名称，此函数调用在调用get_resp函数后，解析为数组前，默认为NULL不调用。
	 * @return array|bool 调用的api接口得到的 JSON,XML生成的数组。
	 */	
	function &get_array($cmdid,array $params,$resp_handle=NULL){
		//parse xml format
		if($this->app_setting['format']=='xml'){  
			$result=& $this->get_resp($cmdid,$params);
			
			if($resp_handle)  //addition handle result 
				try{$result = $resp_handle($result);}catch (Exception  $e){}
				
			$arr=array(); 
			if(!$this->parseXmlString($result,$arr)) return false;
			$this->adjust_xml_result($arr,$this->err_no,$this->err_msg);
			return $arr;
		}
		
		//parse json format
		if(!($result=&$this->get_resp($cmdid,$params))) return false;
		
		if($resp_handle)  //addition handle result 
			try{$result = $resp_handle($result);}catch (Exception  $e){}
		$result = preg_replace('/(":)(\d{9,})/i', '${1}"${2}"', $result); //taobao bug,number >2^32
		
		$arr=json_decode($result,true);
		if ($arr == NULL) { //normal kick invalid json
			$patterns = array ('/,+\s*\}/', '/,+\s*\]/', '/"\s+|\s+"/','/\n|\r/' );
			$replacements = array ('}', ']', '"',' ' );
			$result = preg_replace ( $patterns, $replacements, $result );
			$arr = json_decode ( $result, true );
		}
		$this->adjust_json_result($arr,$this->err_no,$this->err_msg);
		return $arr;
	}	
	
	private function adjust_json_result(array & $arr,& $err_no,& $err_msg){
		if(isset($arr['error_code'])){
			$err_no=$arr['error_code'];
			$err_msg=$arr['error_message'];
			$arr=false;
			return;
		} 
		if(isset($arr['result'])) $arr=$arr['result'];
		else{
			if(isset($arr['code'][0])){
				$err_no=$arr['code'][0];
				$err_msg=$arr['message'][0];	
				$arr=false;	
				return;		
			}
		}
		
	}	

	private function adjust_xml_result(array & $arr,& $err_no,& $err_msg){
		$arr= $arr['Responses']['Response'];
		$err=$arr['Status']['Code'];
		if($err != 200){				//系统错误
			$err_no=$err;
			$err_msg=$arr['Status']['Message'].";\n".$arr['Exception'];
			$arr=false;	
			return;
		}
		$arr=$arr['Result'];
		if( isset($arr['message']) && $arr['message']!='null'){
			$err_no=$arr['code'];
			$err_msg=$arr['message'];
			$arr=false;	
			return;
		}
		if(isset($arr['result'])) $arr=$arr['result'];
	}	
	/*
	 * 签名函数,内部用 
	 */
    protected function _createSign($app_secret,array & $paramArr,$uri_path) {
		$sign= & $this->_to_sign_str($paramArr);
		return strtoupper(hash_hmac('sha1',$uri_path.$sign,$app_secret));
	}
	/**
	 * xml字符串解析为数组，xml字符串如<orders><order orderid="97913707" merchantid="6619" 
	 * paysum="32.90" isdeleted="0" ispaid="0" status="1" merchantorderid="" /></orders>
	 * <br/>如果不是xml字符串，不解析。<br/>原如果解析失败，抛出Execption异常
	 * @param string $xmlString xml字符串
	 * @param array $resultarr 解析后的结果数组
	 */
	function parseXmlString(&$xmlString,array &$arr){
		$xmlString=trim($xmlString);
		if(strpos($xmlString,'<')!==0) return false; //simple test is xml
		try{
		  $root =new SimpleXMLElement($xmlString);
		  $resultarr=array();
		  $this->__fillArray($root,$resultarr);
		  $arr=$resultarr;
		  return true;
		}catch (Exception $e){
			throw new Exception('返回xml解析错误:' . $e->getMessage());
		}
	}	
	//----private function ------//
	function __fillArray($root,array &$resultarr){
		if(! $root) return;
		$rootarr=get_object_vars($root);
		$isList=false;
		foreach ($rootarr as $key=>$val) {
			if($key=='@attributes'){
				$isList=$val['list'];   //is array
			} 
			else if(is_object($val)){
				$newarr=array();
				$this->__fillArray($val,$newarr);
				if($isList)
				  $resultarr[$key]=array($newarr);   //one element array
				else
				   $resultarr[$key]=$newarr;
			}
			else if(is_array($val) && count($val)>0){//is array
				$valarr=array();
				for($i=0;$i<count($val);$i++){
					$newarr=array();
					$this->__fillArray($val[$i],$newarr);
					$valarr[]=$newarr;
				}
				$resultarr[$key]=$valarr;
			}
			else
			 $resultarr[$key]=$val;
		}		
	}
	
}

